package CS4310.csueb.visual;
///////////////////////////////////////////////////////////////////////////
//class MapPanel 
//
//Displays the Map and also the sprites and items that exist on it.
//
//@author jclee
//Created 2011-10-21
//Updated 2011-11-12 by Brian to begin implementing map tiles and scrolling
//Updated 2011-11-27 by Brian to implement saving/restoring of game state for MapPanel
//                                  and modification of game load routine
//Updated 2011-11-28 by Brian to add identifying info for game restore
//                                  and correct bug with offsets
//Updated 2011-11-29 by Brian to add code to load intital tile contents
//
///////////////////////////////////////////////////////////////////////////

import java.awt.image.BufferedImage;
import java.io.*;
import java.awt.*;
import java.awt.event.*;

import javax.imageio.*;
import javax.swing.*; 
import java.io.ObjectOutputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.FileInputStream;
import java.io.IOException;

///////////////////////////// class maps /////////////////////////////////
public class MapPanel extends JPanel implements KeyListener, ActionListener
{
  private int MAPWidth = 12;  // Absolute Max WIDTH for this Map.
  private int MAPHeight = 10;  // Absolute Max HEIGHT for this Map.
  private static final int PORTWIDTH = 8;  // Absolute Max WIDTH for this Map Port.
  private static final int PORTHEIGHT = 5;  // Absolute Max HEIGHT for this Map Port.
  private static final int MAXMAPWIDTH = 1000;
  private static final int MAXMAPHEIGHT = 1000;
  
  // MapTile Qualities
  private enum tileQuality {
    NORMAL, HARMFUL, PROTECTED, DELAY, NOACCESS, 
    NORTHACCESS, NEACCESS, EASTACCESS, SEACCESS, SOUTHACCESS, SWACCESS, WESTACCESS, NWACCESS
  }
  
  private String mapName;
  
  private BufferedImage img;
  private Graphics g;
  private TilePanel tPanel[]; //stores map painted in MapPanel
  private TilePanel tp;
  private static final long serialVersionUID = -3357275291340439271L;
  
  private MapTileContainer MTContainer;
  private MapTile currentMapTile;
  private int paintIndex;
 
  
//********* default constructor **********//  
  public MapPanel() 
  {   
	  setLayout(new GridLayout(5,8));
	  setBackground(Color.BLACK);
	  //setLayout(new GridLayout());
	  //GridBagConstraints c = new GridBagConstraints();
	  setBounds(0, 0, 800, 500);
	  paintIndex = 0;
	  tPanel = new TilePanel[150];
	  //mapName = "Westin";
	  this.setFocusable(true);
	  //this.addKeyListener(this);
  }

//******* overloaded constructor *********//
//takes a file name for an image and map dimensions   
  public MapPanel(String filename) 
  {   
	  this();
	  //loadAndPaint(filename);
  }
  
  
//******* MTCBuildLoader *********//
  public void MTCBuildLoader(String filename)
  {
	  int x, y, lineNumber = 0, row, col;
	  int xOffset = 0, yOffset = 0;
  	  int width = 0, height = 0;
	  BufferedReader textReader1; //file stream for map "texture" text
	  BufferedReader textReader2; //file stream for the tile qualities text
      String fileText1;	//stores filename for map "texture" text
      String fileText2; //stores filename for qualities text
      String s1, s2, fileContent[][];
      
      mapName = filename;
      fileContent = new String[MAXMAPWIDTH][MAXMAPHEIGHT];
	  
	  
      /*
       *  This BuildLoader loads image files that are named with 
       *    distinct tags. 
       *    
       *    X,Y  offsets are calculated in this method
       *    
       *  It is no longer useful, except to load the old test files
       *     if that is ever necessary.
       */
	  try
	  {
		//set and open file streams  
		s1 = String.format("CS4310/csueb/imgdev/Test Images/maptest2/%s.txt", filename);
		textReader1 =  new BufferedReader(new FileReader(s1));
		s2 = String.format("CS4310/csueb/imgdev/Test Images/maptest2/%sTileContent.txt", filename);
		textReader2 =  new BufferedReader(new FileReader(s2));
		
		//tokenizes the tile qualities/occupants text
    	//stores the qualities/occupants
        while ((fileText2 = textReader2.readLine()) != null)   {
      	  String[] fileContentArray = fileText2.split(",");
      	  row = Integer.parseInt(fileContentArray[0]);
      	  col = Integer.parseInt(fileContentArray[1]);
      	  fileContent[row][col] = fileContentArray[2];
        }
		
		//read "texture" file by line
        while ((fileText1 = textReader1.readLine()) != null)   {

        	//takes the first line of the texture map
        	//finds the width and height of the map
        	if ( lineNumber == 0 ) {  // Map Size: width in columns, height in rows
        	  String[] dimensions = fileText1.split(",");
        	  width = Integer.parseInt(dimensions[0]);
        	  height = Integer.parseInt(dimensions[1]);
        	  
        	  if ( width == 0 || height == 0 ) {
      			// Notify User of failure to BuildLoad
      			throw new Exception("The map failed to build. width=0 or heigh=0.");
      			//break;
      		  }
        	  
        	  MTContainer = new MapTileContainer(width, height);
        	  MTContainer.setXOffset(xOffset);
        	  MTContainer.setYOffset(yOffset);
        	  MTContainer.setMapName(mapName);
          	  lineNumber++;
          	  continue;  //should skip the rest of this loop iteration
          	  			 //and continue to next
        	} //end if line 0
        	
        	y = lineNumber-1; 
        	lineNumber++;    
        	
        	//tokenizes/stores the map texture of one line into image tags
      	    String[] imageFileTag = fileText1.split (",");
      	    this.removeAll(); // Clear Previous Map Tiles
      	  
      	    for (int i = 0; i < width; i++) {
      	      x = i; //for clarity and separation I think 
      	      		 //i is in this loop
      	      		 //x for coordinates
      	      
      	      //grabs image associated with image tile tag at current position
			  s1 = String.format("CS4310/csueb/imgdev/Test Images/maptest2/%s%s.png", filename, imageFileTag[i]);
			  
			  currentMapTile = new MapTile(s1); //creates new tile with image
			  currentMapTile.setLocation(x-(MAPWidth/2), y-(MAPHeight/2));

			  
			  //populates qualities map with quality:NONE if applicable
			  if ( fileContent[y][x] == null ) fileContent[y][x] = "NONE";
			  
			  //splits qualities and occupants if applicable
			  //stores "texture" map contents into contentTags
			  String[] contentTags = fileContent[y][x].split(";");
			  
			  //parses the contents of tiles and performs appropriate action
			  for (int j = 0; j < contentTags.length; j++) {
				  String[] contentParam = contentTags[j].split(":");
				  if ( contentParam[0] == "QUALITY" ) {
                    currentMapTile.setQuality(contentParam[1]);
				  }
				  else if ( contentParam[0] == "WEAPON" ) {
	                // Add a Weapon;
				  }
				  else if ( contentParam[0] == "ARMOR" ) {
		                // Add Armor;
				  }
				  else if ( contentParam[0] == "SHOE" ) {
		                // Add a Shoe;
				  }
				  else if ( contentParam[0] == "FOOD" ) {
		                // Add Food;
				  }
				  else if ( contentParam[0] == "KEYITEM" ) {
		                // Add a KeyItem;
			      }
			  }//end for content tags

			  //stores tile in MapTileContainer	
			  MTContainer.storeMapTile(currentMapTile, (int)(x-(MAPWidth/2)), (int)(y-(MAPHeight/2)));
			  if ( x >= xOffset && x < (xOffset+PORTWIDTH) &&
				   y >= yOffset && y < (yOffset+PORTHEIGHT) ) {
				  //paintIndex = i;
				  currentMapTile = MTContainer.retrieveMapTile((int)(x-(MAPWidth/2)), (int)(y-(MAPHeight/2)));
				  
				  /*
				  //stores tiles
				  String str = currentMapTile.getTileImage();
				  tp = new TilePanel(str);
				  //add(tp);
				  tPanel[paintIndex] = tp;   // Store to allow direct manipulation.
				  //success = true;
				   */ 
			  }//end if
      	   }//end while < width
        }//end while !eof  "texture"
		textReader1.close();
		textReader2.close();	
      }
	  catch (Exception e) {
		// Notify User of BuildLoad Failure
	  }
	  
	  //--builds map in viewport--//
	  //currently calls scroll map, will change in the future
	  scrollMap(xOffset, yOffset);
  }
  
  
//******* oldMTCBuildLoader *********//
  public void oldMTCBuildLoader()
  {
      /*
       *  This BuildLoader loads image files that are named with 
       *    tags indicating row and column. 
       *    
       *  It is no longer useful, except to load the old test files
       *     if that is ever necessary.
       */
	  MTContainer = new MapTileContainer();
	  if ( ! oldLoadMTContainer(0,0) ) {

	  }
  }
  
//************* saveGameMap() **************/
  public boolean saveGameMap()
  { 
	  boolean success = false;
	  FileOutputStream fileOutStream = null;
	  ObjectOutputStream objectOutStream = null;
	  
	  /* 
	   *  This routine will persist the current game map.
	   *    It does not matter what map it is. 
	   *    It can be mtcWestin, mtcKraal, mtcFolvani, mtcElito or ... mtcOther
	   *    
	   *    The map that gets saved is the current map in the current MapTileContainer.
	   *    
	   *    Note that this routine only persists the current game Map and its content.
	   *       It does not persist other aspects of the game which must be persisted
	   *        by separate routines managed by GameManager().
	   */
	  try {
	    fileOutStream = new FileOutputStream("mtcsave");
	    objectOutStream = new ObjectOutputStream(fileOutStream);
	    objectOutStream.writeObject(MTContainer);
	    objectOutStream.close();
	 }
	 catch(IOException oe)
	 {
	    // Notify player of game save failure.
	 }
	  
	  return success;
  }
  
//************* restoreGameMap() **************/
  public boolean restoreGameMap()
  {
	  return loadGameMap("mtcsave");
  }
  
//************* loadGameMap(String fileName) **************/
  public boolean loadGameMap(String fileName)
  { 
	  boolean success = false;
	  FileInputStream fileInStream = null;
	  ObjectInputStream objectInStream = null;
	  
	  /*
	   *  Note that the fileName restored is mtcsave when restoring a game
	   *     When loading the default game, the file name would be mtcWestinBaseCamp
	   *        and a param file for map tile characteristics would also be loaded
	   *        and specific tiles would have tile qualities setup.
	   *        
	   *    When changing to different maps in the game the files to be loaded would change.
	   *        mtcWestingBaseCamp
	   *        mtcKraal
	   *        mtcFolvani
	   *        mtcElito
	   *        ... mtcOther
	   */
	  try {
	    fileInStream = new FileInputStream(fileName);
	    objectInStream = new ObjectInputStream(fileInStream);
	    MTContainer = (MapTileContainer) objectInStream.readObject();
	    objectInStream.close();
	    fileInStream.close();
	 }
	 catch(IOException oe)
	 {
	    // Notify player of game save failure.
	 }
	 catch(ClassNotFoundException oe)
	 {
	    // Notify player of game save failure.
	 }
	  
	  return success;
  }  
  
//************* loadAndPaint() **************/
/* 
  public void loadAndPaint(String filename)
  { 
	  
	  //reads in image file to BufferedImage
	  try {
	      img = ImageIO.read(new File(filename));
	      } catch (IOException e) {}
	  //converts BufferedImage to Graphics2d 
	  g = img.getGraphics();
  }
*/

//********* override paintComponent() *********/
 /* 
  public void paintComponent(Graphics g)
  { 
	  super.paintComponent(g);

	  if(img != null){
		  //g.drawImage(img, 0, 0, null);
	  } 
  }
  
  public void paintComponent()
  { 
	  super.paintComponent(g);

	  if(img != null){
		  //g.drawImage(img, 0, 0, null);
	  } 
  }
*/
  
// This method not only does an initial load of the MapTileContainer
// But it also builds the TilePanels and paints them to the Map.
// This will likely change in the future when methods are evaluated to persist the map components. 
  public boolean oldLoadMTContainer(int xOffset, int yOffset) {
	  boolean success = false;
	  String s;
	  //int i;
	  int xMax = (MAPWidth-PORTWIDTH), yMax = (MAPHeight-PORTHEIGHT);
	  
	  // to be safe
	  if ( xOffset < 0 ) xOffset = 0;
	  if ( xOffset > xMax ) xOffset = xMax;
	  if ( yOffset < 0 ) yOffset = 0;
	  if ( yOffset > yMax ) yOffset = yMax;
	  
	  MTContainer.setXOffset(xOffset);
	  MTContainer.setYOffset(yOffset);
	  
	  this.removeAll(); // Clear Previous Map Tiles
	  
	  // Load Up Test Files
	  for ( int y = 0; y < MAPHeight; y++ ) {
		  for ( int x = 0; x < MAPWidth; x++ ) {
			  //s = String.format("CS4310/csueb/imgdev/Test Images/maptest1/testgame1_%02d-%02d.png", y, x);
			  s = String.format("CS4310/csueb/imgdev/Test Images/MapTestTile_%02d-%02d.png", y, x);
			  currentMapTile = new MapTile(s);
			  currentMapTile.setLocation(x-(MAPWidth/2), y-(MAPHeight/2));
			  MTContainer.storeMapTile(currentMapTile, (int)(x-(MAPWidth/2)), (int)(y-(MAPHeight/2)));
			  if ( x >= xOffset && x < (xOffset+PORTWIDTH) &&
				   y >= yOffset && y <= (yOffset+PORTHEIGHT) ) {
				  //paintIndex = i;
				  currentMapTile = MTContainer.retrieveMapTile((int)(x-(MAPWidth/2)), (int)(y-(MAPHeight/2)));
				  String str = currentMapTile.getTileImage();
				  tp = new TilePanel(str);
				  add(tp);
				  tPanel[paintIndex] = tp;   // Store to allow direct manipulation.
				  success = true;
			  }
		  }
	  }
	  repaint();
	  revalidate();
	  return success;
  }
  
  //------------- scrollMap()----------------//
  public boolean scrollMap(int xOffset, int yOffset) {
	  boolean success = false;
	  int i = 0;
	  int xMax = (MAPWidth-PORTWIDTH), yMax = (MAPHeight-PORTHEIGHT);
	  if ( xOffset < 0 ) xOffset = 0;
	  if ( xOffset > xMax ) xOffset = xMax;
	  if ( yOffset < 0 ) yOffset = 0;
	  if ( yOffset > yMax ) yOffset = yMax;
	  
	  MTContainer.setXOffset(xOffset);
	  MTContainer.setYOffset(yOffset);
	  
	  this.removeAll(); // Clear Previous Map Tiles
	    
	  // Load Up Test Files
	  for ( int y = 0; y < MAPHeight; y++ ) {
		  for ( int x = 0; x < MAPWidth; x++ ) {
			  if ( x >= xOffset && x < (xOffset+PORTWIDTH) &&
				   y >= yOffset && y < (yOffset+PORTHEIGHT) ) {
				  //paintIndex = i++;
				  currentMapTile = MTContainer.retrieveMapTile((int)(x-(MAPWidth/2)), (int)(y-(MAPHeight/2)));
				  String str = currentMapTile.getTileImage();
				  tp = new TilePanel(str);
				  add(tp);
				  //this.repaint();
				  //tPanel[paintIndex] = tp;   // Store to allow direct manipulation.
				  success = true;
			  }
		  }
	  }
	  this.repaint();
	  this.revalidate();
	  
	  return success;
  }
  
  
//********* set mapName **********//  
  public void setMapName(String map) 
  {
	mapName = map;
  }
  
//********* get xOffset **********//  
  public int getXOffset()
  {
	return MTContainer.getXOffset();
  }
  
  
//********* get yOffset **********//  
  public int getYOffset()
  {
	return MTContainer.getYOffset();
  }

	//********* key Typed **********//  
  public void keyPressed(KeyEvent e) { 
	  int keyCode = e.getKeyCode();
	  int xOffset = this.getXOffset();
	  int yOffset = this.getYOffset();

	  switch (keyCode) {
	    case 97:
	      xOffset--; yOffset++;
		  break;
	    case 98:
		      yOffset++;
			  break;
	    case 99:
		      xOffset++; yOffset++;
			  break;
	    case 100:
		      xOffset--; 
			  break;
		case 101:
		      xOffset = (MAPWidth-PORTWIDTH)/2;
		      yOffset = (MAPHeight-PORTHEIGHT)/2;
			  break;
	    case 102:
		      xOffset++; 
			  break;
	    case 103:
		      xOffset--; yOffset--;
			  break;
	    case 104:
		      yOffset--;
			  break;
	    case 105:
		      xOffset++; yOffset--;
			  break;
		default:
			break;
	  } 
	  this.scrollMap(xOffset, yOffset);
	  //this.repaint();
  }
   
//********* key Pressed **********//  
  public void keyTyped(KeyEvent e) {
	  //scrollMap(4, 5);
  }
   
//********* key Released **********//  
  public void keyReleased(KeyEvent e) {
	  //scrollMap(4, 5);
  }
  
//********* Action Performed **********//  
  public void actionPerformed(ActionEvent e) {
	  //this.repaint();
  }
  
}



